﻿package net.wangit.jdbc.database;

import net.wangit.jdbc.DB;
import net.wangit.jdbc.JDBCException;
import net.wangit.jdbc.Type;
import net.wangit.jdbc.sqltype.SqlType;
import net.wangit.util.Checker;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * 数据库抽象类
 * 
 * @author watson
 * 
 */
public abstract class Database {

	private static final Log log = LogFactory
			.getLog(net.wangit.jdbc.database.Database.class);
	private String name;
	private final TypeMapping typeMapping = new TypeMapping();

	protected Database() {
		log.info((new StringBuilder("Using database: ")).append(this)
				.toString());
	}

	public String toString() {
		return getClass().getName();
	}

	/*
	 * 默认批量大小
	 */
	public abstract int getDefaultBatchSize();

	/*
	 * 获取SqlType名称
	 */
	public String getSqlTypeName(int typeCode) {
		String result = typeMapping.getSqlTypeName(typeCode);
		if (result == null) {
			throw new UnsupportedOperationException((new StringBuilder(
					"No type mapping for ")).append(typeCode).toString());
		} else {
			return result;
		}
	}

	/*
	 * 获取SqlType代码
	 */
	public abstract int getTypeCode(int i, String s, int j, int k)
			throws JDBCException;

	/*
	 * 获取SqlType
	 */
	public SqlType getSqlType(int typeCode) {
		return typeMapping.getSqlType(typeCode);
	}

	/**
	 * 获取结果个数
	 * 
	 * @param sql
	 * @return
	 */
	public String getCountSql(String sql) {
		return (new StringBuilder("select count(*) from (")).append(sql)
				.append(") tmp_").toString();
	}

	protected void registerType(int typeCode, int sqlTypeCode, String name) {
		typeMapping.put(typeCode, sqlTypeCode, name);
	}

	public boolean hasAlterTable() {
		return true;
	}

	public boolean supportsForUpdate() {
		return true;
	}

	public boolean supportsForUpdateOf() {
		return false;
	}

	public boolean supportsForUpdateNowait() {
		return false;
	}

	public boolean supportsUnique() {
		return true;
	}

	public String getAddColumnString() {
		throw new UnsupportedOperationException(
				"No add column syntax supported by Database");
	}

	public String getAddPrimaryKeyConstraintString(String constraintName) {
		return (new StringBuilder(" add constraint ")).append(constraintName)
				.append(" primary key ").toString();
	}

	public String appendIdentitySelectToInsert(String insertSQL) {
		return null;
	}

	public String getIdentitySelectString() {
		throw new UnsupportedOperationException(
				"Database does not support identity key generation");
	}

	public String getIdentityColumnString() {
		throw new UnsupportedOperationException(
				"Database does not support identity key generation");
	}

	public String getIdentityInsertString() {
		return null;
	}

	public String getSequenceNextValString(String sequenceName) {
		throw new UnsupportedOperationException(
				"Database does not support sequences");
	}

	public String getCreateSequenceString(String sequenceName) {
		throw new UnsupportedOperationException(
				"Database does not support sequences");
	}

	public String getDropSequenceString(String sequenceName) {
		throw new UnsupportedOperationException(
				"Database does not support sequences");
	}

	public String getQuerySequencesString() {
		return null;
	}

	public String getLowercaseFunction() {
		return "lower";
	}

	public boolean supportsLimit() {
		return false;
	}

	public boolean supportsLimitOffset() {
		return supportsLimit();
	}

	/**
	 * 查询分页SQL
	 * 
	 * @param querySelect
	 * @param hasOffset
	 * @return
	 */
	public String getLimitString(String querySelect, boolean hasOffset) {
		throw new UnsupportedOperationException("paged queries not supported");
	}

	public String getLimitString(String querySelect, boolean hasOffset,
			int limit) {
		return getLimitString(querySelect, hasOffset);
	}

	public boolean supportsVariableLimit() {
		return supportsLimit();
	}

	public boolean bindLimitParametersInReverseOrder() {
		return false;
	}

	public boolean bindLimitParametersFirst() {
		return false;
	}

	public boolean useMaxForLimit() {
		return false;
	}

	public char openQuote() {
		return '"';
	}

	public char closeQuote() {
		return '"';
	}

	public String getSchemaSeparator() {
		return ".";
	}

	public String getNullColumnString() {
		return "";
	}

	public String getQueryOption(Type type, String tableAlias, String name,
			String value, boolean isEqual) {
		if (!Checker.isEmpty(tableAlias))
			name = (new StringBuilder(String.valueOf(tableAlias))).append(
					getSchemaSeparator()).append(name).toString();
		value = value.replaceAll("'", "''");
		switch (type.code()) {
		case Type.CHAR:
		case Type.VARCHAR:
		case Type.CLOB:
			if (isEqual)
				return (new StringBuilder(String.valueOf(name))).append(" = '")
						.append(value).append("'").toString();
			else
				return (new StringBuilder(String.valueOf(name))).append(
						" like '%").append(value).append("%'").toString();

		case Type.SMALLINT:
		case Type.INTEGER:
		case Type.BIGINT:
		case Type.FLOAT:
		case Type.DOUBLE:
		case Type.DECIMAL:
			return (new StringBuilder(String.valueOf(name))).append(" = ")
					.append(value).toString();

		case Type.DATE:
		case Type.TIME:
		case Type.TIMESTAMP:
			return (new StringBuilder(String.valueOf(name))).append(" = '")
					.append(value).append("'").toString();
		}
		throw new UnsupportedOperationException((new StringBuilder(
				"Unsupported sqlType: sqlTypeCode=")).append(type.code())
				.append(",columnName=").append(name).toString());
	}

	public String getScopeQueryOption(Type type, String tableAlias,
			String name, String start, String end) {
		if (!Checker.isEmpty(tableAlias))
			name = (new StringBuilder(String.valueOf(tableAlias))).append(
					getSchemaSeparator()).append(name).toString();
		StringBuffer result = new StringBuffer();
		if (start != null)
			start = start.replaceAll("'", "''");
		if (end != null)
			end = end.replaceAll("'", "''");
		switch (type.code()) {
		case Type.CHAR:
		case Type.VARCHAR:
		case Type.CLOB:
			if (!Checker.isEmpty(start))
				result.append(name).append(" >= '").append(start).append("'");
			if (!Checker.isEmpty(end)) {
				if (!Checker.isEmpty(start))
					result.append(" and ");
				result.append(name).append(" <= '").append(end).append("'");
			}
			return result.toString();

		case Type.SMALLINT:
		case Type.INTEGER:
		case Type.BIGINT:
		case Type.FLOAT:
		case Type.DOUBLE:
		case Type.DECIMAL:
			if (!Checker.isEmpty(start))
				result.append(name).append(" >= ").append(start);
			if (!Checker.isEmpty(end)) {
				if (!Checker.isEmpty(start))
					result.append(" and ");
				result.append(name).append(" <= ").append(end);
			}
			return result.toString();

		case Type.DATE:
		case Type.TIME:
			if (!Checker.isEmpty(start))
				result.append(name).append(" >= '").append(start).append("'");
			if (!Checker.isEmpty(end)) {
				if (!Checker.isEmpty(start))
					result.append(" and ");
				result.append(name).append(" <= '").append(end).append("'");
			}
			return result.toString();

		case Type.TIMESTAMP:
			if (!Checker.isEmpty(start)) {
				if (start.length() == 10)
					start = (new StringBuilder(String.valueOf(start))).append(
							" 00:00:00").toString();
				result.append(name).append(" >= '").append(start).append("'");
			}
			if (!Checker.isEmpty(end)) {
				if (!Checker.isEmpty(start))
					result.append(" and ");
				if (end.length() == 10)
					end = (new StringBuilder(String.valueOf(end))).append(
							" 23:59:59").toString();
				result.append(name).append(" <= '").append(end).append("'");
			}
			return result.toString();
		}
		throw new UnsupportedOperationException((new StringBuilder(
				"Unsupported sqlType: sqlTypeCode=")).append(type.code())
				.append(",columnName=").append(name).toString());
	}

	/**
	 * 表是否存在于数据库中
	 * @param db
	 * @param tableName
	 * @return
	 */
	public boolean tableExistent(DB db, String tableName) {
		String sql = (new StringBuilder("select count(*) from ")).append(
				tableName).toString();
		try {
			db.query(sql);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}
